/** 重载实现对象 */
type Toverloading_impl = {
    /**
     * 实现方法对应的参数类型
     * @description 二维数组
     * @description 第一维表示有多少个参数
     * @description 第二维表示第N个参数支持多少种类型
     * @description 第二维如果只支持一种类型，则可以简写不使用第二维。
     */
    types: Array<string | optional | [optional] | Array<string>>,
    /** 实现方法 */
    fn: Function,
};

/** 重载函数类型 */
interface IoverloadingFN {
    (...args: Array<any>): any;
    cache: Map<string, Function>;
}

interface _CF_ {
    /**
     * 创建重载方法生成器
     * @param funcName 方法名
     * @param impls 重载实现数组
     * @returns 重载方法生成器对象
     */
    overloading: (funcName: string, ...impls: Array<Toverloading_impl>) => IoverloadingFN;
}
{
    const NOARGS = U.F.argType.noArgs;
    const ANY = U.F.argType.any;
    const REST = U.F.argType.rest;
    const key_separator = '@';
    const rest_end = key_separator + REST;
    const get_fn = (cache: Map<string, Function>, keys: Array<string>, key: string) => {
        for (let i = 0; i < keys.length; i++) {
            const keyX = keys[i];

            // 从缓存中取实现方法
            const fn = cache.get(keyX);
            apply_fn: if (fn) {
                if (fn === fne) { break apply_fn; }// 缓存的是失败方法，则跳过。
                // 模糊匹配结果缓存，下一次则可以精确匹配到。
                cache.set(key, fn);
                // 调用实现方法
                return fn;
            } else {
                // 缓存失败键
                cache.set(keyX, fne);
            }
        }
        return null;
    };
    // 失败键是根据传入的参数类型精确值+模糊值+剩余参数，与方法实现本身无关，因此可以共用缓存。
    const fne_keys = new Map();
    // 失败报错方法统一处理
    const fne = (funcName: string, key: string, cacheKeys: Array<string>) => {
        throw new NotImplementedError(`function[${funcName}] is implemented by overloading as follows\n\nf(${cacheKeys.join(')\nf(')})\n\nnot implemented by overloading which you call the function as follows\n\nf(${fne_keys.get(key).join(')\nf(')})\n`);
    };
    // 验证参数，以及提取可选项的实际类型。
    const check: (funcName: string, types: Array<any>, fn: Function) => Array<Array<string>> = (funcName, types, fn) => {
        let results = [];
        let result: any = [];
        let lastOptionalIndex = 0;
        for (let i = 0; i < types.length; i++) {
            const typeI = types[i];

            // 数组类型
            if (Array.isArray(typeI)) {
                // 空数组，不设置类型，认定为任意类型。
                if (typeI.length === 0) {
                    result.push(ANY);
                    continue;
                }

                const resultI = [];
                for (let j = 0; j < typeI.length; j++) {
                    const typeJ = typeI[j];

                    // 剩余参数
                    if (typeJ === REST) {
                        if (typeI.length !== 1) {
                            throw new IllegalArgumentError(`function[${funcName}] bad usage for 'RESTARGS' at impl.types[${i}][${j}] when implemnts ${fn.toString()}. rest arguments can not use with other types.`);
                        }
                        if (i !== types.length - 1) {
                            throw new IllegalArgumentError(`function[${funcName}] bad usage for 'RESTARGS' at impl.types[${i}][${j}] when implemnts ${fn.toString()}. only in the end.`);
                        }
                        // 二维只有一项的剩余参数是可以的。
                        resultI.push(typeJ);
                        continue;
                    }

                    // 可选项类型
                    if (typeJ instanceof optional) {
                        if (typeI.length !== 1) {
                            // 可选类型意味着这个参数可有可无，它不应该与确定类型进行组合。此处出现在多种类型中，说明不正确，报错。
                            throw new IllegalArgumentError(`function[${funcName}] bad usage for '${optional.name}' at impl.types[${i}][${j}] when implemnts ${fn.toString()}. optional can not use with other types.`);
                        }
                        // 二维只有一项的可选项类型是可以的。
                        lastOptionalIndex = i + 1;
                        // 将处理完成的结果存放一份，再创建一份新的，存放可选以后的。
                        results.push(result);
                        result = [...result];
                        result.push(typeJ.types);
                        continue;
                    }

                    if (lastOptionalIndex) {// 可选项不能用在其它确定类型之前
                        throw new IllegalArgumentError(`function[${funcName}] bad usage for '${optional.name}' at impl.types[${lastOptionalIndex - 1}] when implemnts ${fn.toString()}. optional can not use before other not optional types.`);
                    }

                    // 无参类型
                    if (typeJ === NOARGS && types.length !== 1) {// 无参类型必须只能有一项
                        throw new IllegalArgumentError(`function[${funcName}] bad usage for 'NOARG' at impl.types[${i}][${j}] when implemnts ${fn.toString()}.`);
                    }

                    // 其它字符串类型
                    resultI.push(typeJ);
                    continue;
                }
                result.push(resultI);

                continue;
            }

            // 剩余参数
            if (typeI === REST) {
                if (i !== types.length - 1) {
                    throw new IllegalArgumentError(`function[${funcName}] bad usage for 'RESTARGS' at impl.types[${i}]${Array.isArray(typeI) ? '[0]' : ''} when implemnts ${fn.toString()}. only in the end.`);
                }
                result.push(typeI);
                continue;
            }

            // 可选项类型
            if (typeI instanceof optional) {
                lastOptionalIndex = i + 1;
                // 将处理完成的结果存放一份，再创建一份新的，存放可选以后的。
                results.push(result);
                result = [...result];
                // 提取可选类型
                result.push(typeI.types);
                continue;
            }

            if (lastOptionalIndex) {// 可选项不能用在其它确定类型之前
                throw new IllegalArgumentError(`function[${funcName}] bad usage for '${optional.name}' at impl.types[${lastOptionalIndex - 1}] when implemnts ${fn.toString()}. optional can not use before other not optional types.`);
            }

            // 无参类型
            if (typeI === NOARGS) {
                if (types.length != 1) {// 无参类型必须只能有一项
                    throw new IllegalArgumentError(`function[${funcName}] bad usage for 'NOARG' at impl.types[${i}]${Array.isArray(typeI) ? '[0]' : ''} when implemnts ${fn.toString()}.`);
                }
                result.push(typeI);
                continue;
            }

            // 其它字符串类型
            if (U.is(Etype.string, typeI)) {
                // 仍然可能不是合法的类型名，但是不影响映射以及执行
                result.push(typeI);
                continue;
            }

            // 不支持的类型
            throw new IllegalArgumentError(`function[${funcName}] impl.types[${i}] must be array or string or optional, actual is ${typeI} when implemnts ${fn.toString()}.`);
        }
        results.push(result);
        return results;
    };
    Object.defineProperty(_CF_.prototype, 'overloading', {
        writable: false,
        configurable: false,
        enumerable: false,
        value: function (funcName: string, ...impls: Array<Toverloading_impl>): IoverloadingFN {
            if (!U.is(Etype.string, funcName)) { throw new IllegalArgumentError(`funcName must be a string, actual is ${U.typeof(funcName)}.`); }

            const cache: Map<string, Function> = new Map();// 重载方法实现缓存集合

            // 添加重载方法实现到缓存中
            impls.forEach(impl => {
                if (!U.is(Etype.function, impl.fn)) { throw new IllegalArgumentError(`function[${funcName}] impl.fn must be a function, actual is ${impl.fn}.`); }
                if (!U.is([Etype.array, '[object optinal]'], impl.types)) { throw new IllegalArgumentError(`function[${funcName}] impl.types must be array, actual is ${impl.types} when implemnts ${impl.fn.toString()}.`); }

                if (impl.types.length === 0) {// 无参方法
                    cache.set(NOARGS, impl.fn);
                } else {// 有参方法
                    check(funcName, impl.types, impl.fn).forEach(arr => {
                        if (arr.length === 0) { cache.set(NOARGS, impl.fn); }// 数组为空时，意味着是无参方法。
                        U.A.combination(arr, key_separator).forEach(key => {
                            cache.set(key, impl.fn);
                            if (key.endsWith(rest_end)) {// 有剩余参数的，多设置一份不带剩余参数的。
                                cache.set(key.slice(0, -rest_end.length), impl.fn);
                            }
                        });
                    });
                }
            });

            // 记录已经实现的键
            const cacheKeys: Array<string> = [];
            cache.forEach((value, key) => { cacheKeys.push(key); });

            /**
             * 重载方法实际执行方法
             * @description 将参数列表的类型拼值接作为键，从重载方法集合中找到已经实现的具体重载方法执行。
             * @param args 调用方法时的参数列表
             * @returns 重载方法执行结果
             */
            const exe = function (this: any, ...args: Array<any>) {
                const types = args.map(arg => U.typeof(arg, false));

                const key = types.join(key_separator);
                // 精确匹配
                {
                    // 从缓存中取实现方法
                    let fn = cache.get(key);
                    if (fn) {
                        if (fn === fne) { fne(funcName, key, cacheKeys); }// 缓存的是失败方法，则直接报错，无需在此模糊匹配。
                        // 调用实现方法
                        return fn.apply(this, args);
                    } else {
                        // 缓存失败键
                        cache.set(key, fne);
                    }
                }

                // 任意参数匹配
                let keys_fuzzy;
                // 剩余参数匹配
                let keys_rest;
                if (types.length === 0) {// 无参处理 优先匹配单值剩余参数。
                    keys_rest = [REST];
                    {
                        const fn = get_fn(cache, keys_rest, key);
                        if (fn) { return fn.apply(this, args); }
                    }
                    keys_fuzzy = [ANY];
                    {
                        const fn = get_fn(cache, keys_fuzzy, key);
                        if (fn) { return fn.apply(this, args); }
                    }

                    // 最终没有匹配成功
                    // 缓存对应的失败键
                    fne_keys.set(key, [key, ...keys_rest, ...keys_fuzzy]);
                } else {// 其它参数处理，优先匹配任意参数。
                    keys_fuzzy = U.A.combination(types.map(t => [t, ANY]), key_separator).slice(1);// 组合键的第一个是精确匹配，需要排除掉。
                    {
                        const fn = get_fn(cache, keys_fuzzy, key);
                        if (fn) { return fn.apply(this, args); }
                    }
                    keys_rest = types.map((x, i) => types.slice(0, types.length - i - 1).concat(REST as Etype).join(key_separator));
                    {
                        const fn = get_fn(cache, keys_rest, key);
                        if (fn) { return fn.apply(this, args); }
                    }

                    // 最终没有匹配成功
                    // 缓存对应的失败键
                    fne_keys.set(key, [key, ...keys_fuzzy, ...keys_rest]);
                }

                // 报错
                fne(funcName, key, cacheKeys);
            }

            // 暴露缓存信息，方便调试。
            exe.cache = cache;

            // 返回重载对象
            return exe;
        }
    });
}
